package vip.fuxk.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeBuilder;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.parser.NodeParser;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import freemarker.ext.beans.HashAdapter;
import org.apache.ibatis.solon.annotation.Db;
import org.noear.solon.Solon;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Mapping;
import org.noear.solon.core.handle.Context;
import org.noear.solon.data.cache.CacheService;
import org.smartboot.http.server.impl.HttpRequestImpl;
import vip.fuxk.blog.common.R;
import vip.fuxk.blog.dao.DataMapper;
import vip.fuxk.blog.data.DataMetaInfoVo;
import vip.fuxk.blog.data.DataMetaVo;
import vip.fuxk.blog.data.DataQueryFieldValue;
import vip.fuxk.blog.data.DataVo;
import vip.fuxk.blog.service.DataService;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;


@Component
public class DataServiceImpl implements DataService {
    @Db
    DataMapper dataMapper;

    @Inject
    CacheService cacheService;

    public static TreeNodeConfig TREE_DEFAULT_CONFIG = new TreeNodeConfig();

    static {
        TREE_DEFAULT_CONFIG.setIdKey("data_id");
        TREE_DEFAULT_CONFIG.setParentIdKey("parent_id");
        TREE_DEFAULT_CONFIG.setNameKey("name");
        TREE_DEFAULT_CONFIG.setWeightKey("sort_num");
        TREE_DEFAULT_CONFIG.setDeep(null);
    }

    public DataMetaVo getMetaInfo(String data_name){
        return dataMapper.getMetaInfoByDataName(data_name,null);
    }
    public DataMetaVo getMetaInfo(Long id){
        return dataMapper.getMetaInfoByDataName(null,id);
    }


    public Object getDataByName(String data_name, Object... value){
        return this.getDataByNameBase(data_name,true,value);
    }

    public List<DataQueryFieldValue> getPramsList(DataMetaVo dataMetaVo){
        Context ctx =  Context.current();
        Object requestObj =  ctx.request();
        List<DataQueryFieldValue> qs = null;
        Set<String> field_names = Arrays.stream(ReflectUtil.getFields(DataVo.class))
                .filter(it-> !Modifier.isStatic(it.getModifiers()))
                .map(Field::getName).collect(Collectors.toSet());
        if( requestObj instanceof HttpRequestImpl  ){
            HttpRequestImpl request = (HttpRequestImpl) requestObj;
            Map<String,String[]> parameters = request.getParameters();
            if(ObjUtil.isNotEmpty(parameters)){
                List<DataMetaInfoVo> metaInfoVos =  dataMetaVo.meta_info;
                if(ObjectUtil.isNotEmpty(metaInfoVos)){
                    Set<String> mset = metaInfoVos.stream()
                            .map(DataMetaInfoVo::getField)
                            .filter(ObjUtil::isNotEmpty)
                            .collect(Collectors.toSet());
                    if(ObjectUtil.isNotEmpty(mset)){
                        field_names.addAll(mset);
                    }
                }
                for (String field : field_names) {
                    if(ObjUtil.isNotEmpty(field)){
                        String[] value = parameters.get(field);
                        if(ObjectUtil.isNotEmpty(value)){
                            List<Object> valueList =  Arrays.stream(value).filter(ObjectUtil::isNotEmpty)
                                    .collect(Collectors.toList());
                            if(ObjectUtil.isEmpty(valueList)){
                                continue;
                            }
                            if(qs ==null){
                                qs = new ArrayList<>();
                            }
                            DataQueryFieldValue qv = new DataQueryFieldValue();
                            qv.field = field;
                            String op = field+"-op";
                            String[] opParam = parameters.get(op);
                            if( ObjectUtil.isNotEmpty(opParam) && ObjectUtil.isNotEmpty(opParam[0])){
                                qv.query_type = opParam[0];
                            }
                            qv.value = valueList;
                            qs.add(qv);
                        }
                    }
                }
            }
        }

        int limit = ctx.paramAsInt("limit",10);
        int page = ctx.paramAsInt("page",1);
        if(page<=0){
            page = 1;
        }
        if(limit<10){
            limit = 10;
        }
        if(limit>2000){
            limit = 2000;
        }
        if(qs == null){
            qs = new ArrayList<>();
        }
        DataQueryFieldValue pageParam = new DataQueryFieldValue();
        pageParam.page =page;
        pageParam.limit =limit;
        qs.add(pageParam);
        return qs;
    }


    public Object[] getPrams(DataMetaVo dataMetaVo ){
        List<DataQueryFieldValue> qs = this.getPramsList(dataMetaVo);
        Object[] qsarr = null;
        if(ObjectUtil.isNotEmpty(qs)){
            qsarr = new Object[qs.size()];
            qsarr = qs.toArray(qsarr);
        }
        return qsarr;
    }

    @Override
    public Object getDataByNameNoCache(String data_name) {
        DataMetaVo dataMetaVo =  this.getMetaInfo(data_name);
        if(dataMetaVo == null){
            return null;
        }
        return this.getDataByNameBase(data_name,false,this.getPrams(dataMetaVo));
    }

    public Object getDataByNameBase(String data_name,Boolean useCache, Object... value){
        if(data_name ==null){
            data_name = "_";
        }
        List<DataQueryFieldValue> qs =null;
        if(ObjectUtil.isNotEmpty(value)){
            JSONArray jsonArr = JSONUtil.parseArray(value);
            qs = jsonArr.toList(DataQueryFieldValue.class);
        }

        String cacheKey = "data_cache:"+data_name+(qs!=null? ":"+JSONUtil.parseArray(qs).toString():"");
        if(BooleanUtil.isTrue(useCache)){
            Object  cache = cacheService.get(cacheKey);
            if(cache !=null){
                return cache;
            }
        }

        DataMetaVo dataMetaVo =  this.getMetaInfo(data_name);
        if(dataMetaVo == null){
            return null;
        }
        Object result;

        List<DataVo> dataVoList = dataMapper.getDataByDataNameAndCondition(dataMetaVo,qs);
        if(dataMetaVo == null){
            return null;
        }
        result =dataVoList;
        if(dataMetaVo.is_tree){
            TreeBuilder<Long> treeBuilder = new TreeBuilder<>(0L, TREE_DEFAULT_CONFIG);
            treeBuilder.append(dataVoList,0L,   (o, tree) -> {
                if(o != null){
                    o.forEach(tree::putExtra);
                }
            });
            result =  treeBuilder.build().getChildren() ;
        }

        if(dataMetaVo.is_obj){
            if(ObjectUtil.isNotEmpty(result) && result != null){
                result = ((List<?>) result).get(0);
            }else{
                result = new HashMap<>();
            }
        }

        if(BooleanUtil.isTrue(useCache)) {
            if (dataMetaVo.expire > 0) {
                cacheService.store(cacheKey, result, dataMetaVo.expire);
            }
        }

        return result ;
    }

    @Override
    public Long getDataLengthByDataNameAndCondition(String data_name) {
        DataMetaVo dataMetaVo =  this.getMetaInfo(data_name);
        if(dataMetaVo == null){
            return 0L;
        }
        return dataMapper.getDataLengthByDataNameAndCondition(dataMetaVo, this.getPramsList(dataMetaVo));
    }

}
